// (c) Copyright Microsoft Corporation.
// this source is subject to the Microsoft Permissive License.
// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
// All other rights reserved.


/// <reference name="MicrosoftAjax.debug.js" />
/// <reference name="MicrosoftAjaxTimer.debug.js" />
/// <reference name="MicrosoftAjaxWebForms.debug.js" />
/// <reference path="../ExtenderBase/BaseScripts.js" />
/// <reference path="../Common/Common.js" />
/// <reference path="../Compat/Timer/Timer.js" />
/// <reference path="../Animation/Animations.js" />


// Some of the techniques used in this.script were adapted with permission from Bertrand
// LeRoy's MIX06 Demo (http://weblogs.asp.net/bleroy/archive/2006/03/28/441343.aspx)

Type.registerNamespace('AjaxControlToolkit');

AjaxControlToolkit.AutoSize = function() {
	/// <summary>
	/// The AutoSize enumeration is used to specify how the AccordionBehavior limits
	/// the growth of the accordion when panes are expanded and collapsed.  It must
	/// correspond to the AutoSize CLR enumeration in AutoSize.cs.
	/// </summary>
	/// <field name="None" type="Number" integer="true">
	/// Allow the accordion to expand/collapse without restriction.
	/// </field>
	/// <field name="Fill" type="Number" integer="true">
	/// Keep the accordion the same size as its specified size.  If any
	/// panes are larger or smaller than the available space, grow or shrink
	/// them to the available space.
	/// </field>
	/// <field name="Limit" type="Number" integer="true">
	/// Prevent the accordion from growing any larger than its specified size.
	/// If the content of a pane is too large to fit, grow it to fill the
	/// remaining space.
	/// </field>
	throw Error.invalidOperation();
}
AjaxControlToolkit.AutoSize.prototype = {
	None: 0,
	Fill: 1,
	Limit: 2
}
AjaxControlToolkit.AutoSize.registerEnum("AjaxControlToolkit.AutoSize", false);


AjaxControlToolkit.AccordionSelectedIndexChangeEventArgs = function(oldIndex, selectedIndex) {
	/// <summary>
	/// Event arguments used to provide notification when an accordion's selected
	/// index is changed.  The same event argument type is used for both the
	/// selectedIndexChanging event and the selectedIndexChanged events.  If you set
	/// the cancel property to true during the selectedIndexChanging event, the
	/// accordion will not change panes.  The cancel property has no effect during
	/// the selectedIndexChanged event.
	/// </summary>
	/// <param name="oldIndex" type="Number" integer="true" mayBeNull="false">
	/// Last selected index
	/// </param>
	/// <param name="selectedIndex" type="Number" integer="true" mayBeNull="false">
	/// New selected index
	/// </param>
	AjaxControlToolkit.AccordionSelectedIndexChangeEventArgs.initializeBase(this);

	this._oldIndex = oldIndex;
	this._selectedIndex = selectedIndex;
}
AjaxControlToolkit.AccordionSelectedIndexChangeEventArgs.prototype = {
	get_oldIndex: function() {
		/// <value type="Number" integer="true" mayBeNull="false">
		/// Last selected index
		/// </value>
		return this._oldIndex;
	},
	set_oldIndex: function(value) {
		this._oldIndex = value;
	},

	get_selectedIndex: function() {
		/// <value type="Number" integer="true" mayBeNull="false">
		/// New selected index
		/// </value>
		return this._selectedIndex;
	},
	set_selectedIndex: function(value) {
		this._selectedIndex = value;
	}
}
AjaxControlToolkit.AccordionSelectedIndexChangeEventArgs.registerClass('AjaxControlToolkit.AccordionSelectedIndexChangeEventArgs', Sys.CancelEventArgs);



// The Accordion layout is slightly complex because we support several AutoSize modes
// that are applied to an element collection across multiple events and animations.
// To make the code a little easier to understand, we'll provide a brief description
// of the element format, the control flow for laying out the elements, and an informal
// "correctness proof" of our algorithm.
//
// The Accordion server control will create a collection of panes which it will dump
// out as a sequence of <div>s where the (2i)th <div> is the ith header and the
// (2i+1)th <div> is the ith content section:
// 
//      <div id="Accordion" ... >
//          ...
//          <div id="2i" ... >Ith Header</div>
//          <div id="2i+1" ... >Ith Content</div>
//          ...
//      </div>
// 
// Because the <div>s may have padding, borders, or margins (we'll refer to these
// additional pixels as the "gutter"), we cannot collapse them all the way during
// animation (i.e. myDiv.style.height = '0px' will still show myDiv's padding,
// borders, and margins).  To work around this, we wrap all of the content section
// <div>s in wrappers that have no "gutter" style to end up with a tree of DOM
// elements that looks like:
// 
//      <div id="Accordion" ... >
//          ...
//          <div id="2i" ... >Ith Header</div>
//          <div id="2i+1 Wrapper">
//              <div id="2i+1 Original" ... >Ith Content</div>
//          </div>
//          ...
//      </div>
// 
// We automatically wrap the content section when it is passed to the addPane
// function.  From here on out, we'll refer to the behavior's target element (the
// root <div>) as the accordion, the dynamically created <div> as a wrapper, and
// the content section <div> as the original <div>.
//
// The primary purpose of the accordion is to expand and collapse its panes so that
// at most only one pane is open at a time.  We want to do this using animations for
// a polished effect, but we must also respect the AutoSize modes (where None allows
// the accordion to grow unrestrained, Limit provides a maximum height for the
// accordion, and Fill forces the accordion to always be the specified size).  Given
// the structure of an accordion, original <div>s, and wrapper <div>s, we accomplish
// the layout requirements using the following CSS properties:
//      Accordion root <div>:
//          height
//          overflow
//      Wrapper <div>:
//          height
//          opacity
//          overflow
//          display
//      Original content section <div>:
//          height
//          maxHeight
//          overflow
// 
// The large table below precisely defines the expected value of the properties at
// any given point in time.  Each of the columns 1 through 8 represents a possible
// state of the accordion.  Most of the time the accordion will be in states 1 and 5
// until the user clicks a header section and triggers a series of changes.  The rows
// of the table represent the properties of elements in a particular AutoSize mode.
// For example, a closed wrapper <div> with the Limit AutoSize mode should have a value
// of '0px' for its height.  Most of the properties in the the table are literal values
// like 'auto', 'hidden', '0px', etc.  The value Current for a property is used to
// indicate that a height set to 'auto' should be assigned its explicit value (i.e.
// element.offsetHeight) so that it will not be able to grow/shrink during an animation.
// The value Remaining for a height property refers specifically to the available space
// remaining after all the header sections of the accordion have been taken into
// consideration (this is used specifically for setting the size of the open content
// section when working in Limit or Fill AutoSize modes).  It's important to note that
// the Remaining value, retrieved by calling _getRemainingHeight, is slightly different
// for a wrapper <div> compared to a an original <div> because it must also consider the
// "gutter" offsets for an original <div>.  Finally, the animations (columns 3 and 7)
// are represented as transformations that take one property value to another when played.
//
//    ------------------------+---------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------+
//                            |  +---                         Closing an open pane                                    |                              Opening a closed pane                          <--+  |
//        AutoSize Mode       |  |        1.              2.                  3.                  4.                  |       5.              6.                  7.                  8.               |  |
//            Element         |  +-->     Opened   ->     Before Closing  ->  Closing      ->     After Closing     --+->     Closed    ->    Before Opening  ->  Opening     ->      After Opening ---+  |
//                Property    |           Pane            Animation           Animation           Animation           |       Pane            Animation           Animation           Animation           |
//    ------------------------+---------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------+
//        None:               |                                                                                       |                                                                                   |
//            Accordion:      |                                                                                       |                                                                                   |
//                height:     |           'auto'          .                   .                   .                   |       'auto'          .                   .                   .                   |
//                overflow:   |           'auto'          .                   .                   'auto'              |       'auto'          .                   .                   'auto'              |
//            Wrapper:        |                                                                                       |                                                                                   |
//                height:     |           'auto'          .                   Current -> '0px'    .                   |       '0px'           .                   '0px' -> Current    'auto'              |
//                opacity:    |           1.0             .                   1.0 -> 0.0          .                   |       0.0             .                   0.0 -> 1.0          .                   |
//                overflow:   |           'auto'          'hidden'            .                   .                   |       'hidden'        .                   .                   'auto'              |
//                display:    |           'block'         .                   .                   'none'              |       'none'          'block'             .                   .                   |
//            Original:       |                                                                                       |                                                                                   |
//                height:     |           'auto'          .                   .                   .                   |       'auto'          .                   .                   .                   |
//                maxHeight:  |           ''              .                   .                   .                   |       ''              .                   .                   .                   |
//                overflow:   |           'auto'          'hidden'            .                   .                   |       'hidden'        .                   .                   'auto'              |
//    ------------------------+---------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------+
//        Limit:              |                                                                                       |                                                                                   |
//            Accordion:      |                                                                                       |                                                                                   |
//                height:     |           Current         .                   .                   .                   |       Current         .                   .                   .                   |
//                overflow:   |           'hidden'        .                   .                   .                   |       'hidden'        .                   .                   .                   |
//            Wrapper:        |                                                                                       |                                                                                   |
//                height:     |           'auto'          Remaining           Remaining -> '0px'  .                   |       '0px'           .                   '0px' -> Remaining  'auto'              |
//                opacity:    |           1.0             .                   1.0 -> 0.0          .                   |       0.0             .                   0.0 -> 1.0          .                   |
//                overflow:   |           'auto'          'hidden'            .                   .                   |       'hidden'        .                   .                   'auto'              |
//                display:    |           'block'         .                   .                   'none'              |       'none'          'block'             .                   .                   |
//            Original:       |                                                                                       |                                                                                   |
//                height:     |           'auto'          .                   .                   .                   |       'auto'          .                   .                   .                   |
//                maxHeight:  |           Remaining       'auto'              .                   .                   |       'auto'          .                   .                   Remaining           |
//                overflow:   |           'auto'          'hidden'            .                   .                   |       'hidden'        .                   .                   'auto'              |
//    ------------------------+---------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------+
//        Fill:               |                                                                                       |                                                                                   |
//            Accordion:      |                                                                                       |                                                                                   |
//                height:     |           Current         .                   .                   .                   |       Current         .                   .                   .                   |
//                overflow:   |           'hidden'        .                   .                   .                   |       'hidden'        .                   .                   .                   |
//            Wrapper:        |                                                                                       |                                                                                   |
//                height:     |           'auto'          .                   .                   .                   |       'auto'          .                   .                   .                   |
//                opacity:    |           1.0             .                   1.0 -> 0.0          .                   |       0.0             .                   0.0 -> 1.0          .                   |
//                overflow:   |           'auto'          'hidden'            .                   .                   |       'hidden'        .                   .                   'auto'              |
//                display:    |           'block'         .                   .                   'none'              |       'none'          'block'             .                   .                   |
//            Original:       |                                                                                       |                                                                                   |
//                height:     |           Remaining       .                   Remaining -> '0px'  .                   |       '0px'           .                   '0px' -> Remaining  .                   |
//                maxHeight:  |           ''              .                   .                   .                   |       ''              .                   .                   .                   |
//                overflow:   |           'auto'          'hidden'            .                   .                   |       'hidden'        .                   .                   'auto'              |
//    ------------------------+---------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------+
//
// Although this table is rather large, it's extremely useful because it allows
// us to present an informal argument for the correctness of our layout algorithm.
// It's easy to verify that if an accordion starts in state 1 and proceeds through
// steps 2, 3, and 4 when closing, it will arrive properly in state 5.  Similarly
// an accordion starting in state 5 that is opened will progress through steps 6, 7,
// and 8 to arrive back at step 1.  Clearly then any accordion starting in a valid
// state of 1 or 5 can be opened or closed repeatedly and return to a proper state.
// It's also worth pointing out that this layout algorithm can operate on one or two
// panes at a time (i.e. if all panes were closed and one were clicked, it would
// open a single pane...  but if one pane were already open and another were clicked,
// it would have to close one while opening the other).
//
// States 1 and 5 from the above table are implemented in the _intiailizeLayout and
// _initializePane functions.  States 2 and 6 are handled by the _startPaneChange
// function while states 4 and 8 are handled by the _endPaneChange function.  Finally,
// states 3 and 7 are intialized and played in the _changePanes function.



AjaxControlToolkit.AccordionBehavior = function(element) {
	/// <summary>
	/// The AccordionBehavior is used to turn properly structured XHTML into an
	/// Accordion with panes that can expand one at a time.
	/// </summary>
	/// <param name="element" type="Sys.UI.DomElement" domElement="true">
	/// The DOM element the behavior is associated with.  It should contain an
	/// even number of child divs (such that ith pane has a header at div 2i and
	/// has content at div 2i+1).
	/// </param>
	AjaxControlToolkit.AccordionBehavior.initializeBase(this, [element]);

	// The _selectedIndex variable is used to track the currently visible content
	// pane.  It is persisted via ClientState so that it can be restored on PostBack.
	// If 0 <= _selectedIndex < _panes.Length is not true, then no pane is selected
	// (and they all appear collapsed).  While any index outside the bounds of the
	// _panes array indicates that no pane is selected, we don't automatically set
	// the value to a sentinel like -1 (especially on the server) because it's
	// possible for additional panes to be added at any time.  We abstract this
	// problem using the get_Pane() function which returns the selected pane when
	// it's called with no arguments (and returns null when the current selected
	// index is invalid).
	this._selectedIndex = 0;

	// The _panes array represents the collection of Accordion panes.  Each element of
	// the array is an object of the form {header, content, animation} corresponding
	// to that pane's header section, content section, and the animation used to open
	// and close its content section.  The content element is a new div that has been
	// created to wrap the original div (so we can completely collapse it - even if it
	// has padding, margins, etc.) which is pointed to by a dynamic _original property.
	// The header element has a dynamic _index property indicating its position in the
	// Accordion's pane collection (used primarily by the headers' shared click handler).
	// Furthermore, the animation will either be an instance of LengthAnimation or
	// ParallelAnimation (in the latter case, it will have two children which are a
	// LengthAnimation and a FadeAnimation).  There will be two dynamic properties
	// _length and _fade pointing to each of these children (to easily set the length
	// and fadeEffect properties).  There is also a dynamic _ended property which is
	// an event handler to be fired when the animation is complete, a dynamic _opening
	// property to indicate whether the animation was opening or closing the pane, and
	// a dynamic _pane property to provide a reference to the pane that was being
	// animated.
	this._panes = [];

	// The this._fadeTransitions flag determines whether or not we enable a simple fade
	// animation effect on top of the opening and closing effect
	this._fadeTransitions = false;

	// The this._duration represents the transition duration of the animations in seconds
	this._duration = 0.25;

	// framesPerSecond is used to tune the animation to perform well depending on
	// the the type of effect being used an the number of accordion panes, etc.
	this._framesPerSecond = 30;

	// Determine how growth of the Accordion will be controlled.  If it is set to
	// None, then the Accordion can grow as large or as small as necessary.  If it is
	// set to Limit, then the Accordion will always be less than or equal to its
	// available space.  If it is set to Fill, then it will always be equal to its
	// available space.
	this._autoSize = AjaxControlToolkit.AutoSize.None;

	// Whether or not clicking the header will close the currently opened pane (which
	// leaves all the Accordion's panes closed)
	this._requireOpenedPane = true;

	// Whether or not we suppress the client-side click handlers of any elements
	// (including server controls like Button or HTML elements like anchor) in the header
	// sections of the Accordion
	this._suppressHeaderPostbacks = false;

	// Size of all the headers
	this._headersSize = 0;

	// The _headerClickHandler is a reference to the event handler that all the header
	// elements of our panes will be wired up to
	this._headerClickHandler = null;

	// The _headerSelectedCssClass is the css class applied to the selected header.    
	this._headerCssClass = '';

	// The _headerSelectedCssClass is the css class applied to the selected header.    
	this._headerSelectedCssClass = '';

	// The _headerSelectedCssClass is the css class applied to the selected header.    
	this._contentCssClass = '';

	// The _resizeHandler is a reference to the global event handler used to patch
	// up the accordion when the window is resized
	this._resizeHandler = null;
}
AjaxControlToolkit.AccordionBehavior.prototype = {
	initialize: function() {
		/// <summary>
		/// The initialize function is responsible for getting the selected index from
		/// the ClientState mechanism and walking the children of the behavior's target
		/// to find all of the accordion's child panes.  It builds up a collection of the
		/// panes from the headers and content sections.  Then we hide all the content
		/// sections that aren't selected and initialize the layout.
		/// </summary>
		/// <returns />
		AjaxControlToolkit.AccordionBehavior.callBaseMethod(this, 'initialize');

		// Create the onclick handler used by the accordion's headers
		this._headerClickHandler = Function.createDelegate(this, this._onHeaderClick);

		// Get the selected index from ClientState
		var state = this.get_ClientState();
		if (state !== null && state !== '') {
			this._changeSelectedIndex(parseInt(state), false, true);
		}

		// Walk the children of the target control to obtain the accordion's child panes.
		// We are expecting a hierarchy of divs that looks like:
		//   <div id="accordion">         this.element
		//     ...
		//     <div id="header_i"></div>   this.element.childNodes[2i]
		//     <div id="content_i"></div>  this.element.childNodes[2i+1]
		//     ...
		//   </div>
		// We'll turn this hierarchy of divs into objects filling the _panes collection.
		// See the comment above the _panes array for more details on the structure of
		// these objects.  It's also worth pointing out that we effectively "box" the
		// index so it can be passed by reference to the _getNextDiv function
		var nodes = this.get_element().childNodes;
		var index = {};
		for (index.value = 0; index.value < nodes.length; index.value++) {
			var header = this._getNextDiv(nodes, index);
			if (!header) {
				break;
			}
			var content = this._getNextDiv(nodes, index);
			if (content) {
				// Add the pane once we've found both a header and a content section
				// (but bump the loop index back so we don't skip an element when the
				// loop increments)
				this.addPane(header, content);
				index.value--;
			}
		}

		// Ensure we have an opened pane if we're required to (and use the first
		// pane if we don't have a valid selected index)
		if (this._requireOpenedPane && !this.get_Pane() && this._panes.length > 0) {
			this._changeSelectedIndex(0, false, true);
		}

		// Setup the layout for the given AutoSize mode
		this._initializeLayout();
	},

	_getNextDiv: function(nodes, index) {
		/// <summary>
		/// Get the next div in a sequence of child nodes starting at the
		/// given index
		/// </summary>
		/// <param name="nodes" type="Array" mayBeNull="false" elementMayBeNull="true"
		///   elementType="Sys.UI.DomElement" elementDomElement="true">
		/// Array of child nodes (i.e. element.childNodes)
		/// </param>
		/// <param name="index" type="Object" mayBeNull="false">
		/// The index is an object of the form { value } where index.value represents
		/// the current index in the collection of nodes.  We wrap the index in an object
		/// to perform the .NET equivalent of boxing so it can be passed by reference.
		/// </param>
		/// <returns type="Sys.UI.DomElement" DomElement="true" mayBeNull="true">
		/// The next DOM element representing a div tag, starting at the provided index.
		/// </returns>

		var div = null;
		while (index.value < nodes.length && (div = nodes[index.value++])) {
			if (div.tagName && (div.tagName.toLowerCase() === 'div')) {
				break;
			}
		}
		return div;
	},

	addPane: function(header, content) {
		/// <summary>
		/// Create a new Accordion pane given references to its header and content divs
		/// and add it to the _panes collection.  We also wrap the content div in a new
		/// container div, add a click handler to the header div, etc.
		/// </summary>
		/// <param name="header" type="Sys.UI.DomElement" domElement="true" mayBeNull="false">
		/// Header element of the new Accordion pane
		/// </param>
		/// <param name="content" type="Sys.UI.DomElement" domElement="true" mayBeNull="false">
		/// Content element of the new Accordion pane
		/// </param>
		/// <returns type="Object" mayBeNull="false">
		/// New pane object added to the end of the Accordion's pane collection.  The pane
		/// is an object of the form {header, content, animation} corresponding to that
		/// pane's header section, content section, and the animation used to open and
		/// close its content section.  The content element is a new div that has been
		/// created to wrap the original div (so we can completely collapse it - even if it
		/// has padding, margins, etc.) which is pointed to by a dynamic _original property.
		/// The header element has a dynamic _index property indicating its position in the
		/// Accordion's pane collection (used primarily by the headers' shared click handler).
		/// Furthermore, the animation will either be an instance of LengthAnimation or
		/// ParallelAnimation (in the latter case, it will have two children which are a
		/// LengthAnimation and a FadeAnimation).  There will be two dynamic properties
		/// _length and _fade pointing to each of these children (to easily set the length
		/// and fadeEffect properties).  There is also a dynamic _ended property which is
		/// an event handler to be fired when the animation is complete, a dynamic _opening
		/// property to indicate whether the animation was opening or closing the pane, and
		/// a dynamic _pane property to provide a reference to the pane that was being
		/// animated.
		/// </returns>

		// Create the new pane object
		var pane = {};
		pane.animation = null;

		// Initialize the header
		pane.header = header;
		header._index = this._panes.length;
		$addHandler(header, "click", this._headerClickHandler);

		// Wrap the content in a new element
		var accordion = this.get_element();
		var wrapper = document.createElement('div');
		accordion.insertBefore(wrapper, content);
		wrapper.appendChild(content);
		wrapper._original = content;
		pane.content = wrapper;

		// Remove any style facets (possibly) automatically applied by
		// CSS selectors so they don't interfere with UI/layout
		wrapper.style.border = '';
		wrapper.style.margin = '';
		wrapper.style.padding = '';

		// Add the new pane at the bottom of the accordion
		Array.add(this._panes, pane);

		// Setup the layout attributes for the pane so that it will be in a proper opened or
		// closed state (we don't bother setting the opacity of the wrapper with
		// $common.setElementOpacity(wrapper, selected ? 1 : 0); because it will
		// be taken care of by the animation)
		this._initializePane(header._index);

		// Since the content section of the accordion panes will be sent down from the server
		// with display: none (so the content sections aren't shown before they're wrapped in
		// new divs) we'll turn them back on once they've been wrapped in hidden divs
		content.style.display = 'block';

		return pane;
	},

	_getAnimation: function(pane) {
		/// <summary>
		/// Get the animation for the specified accordion section or demand create
		/// the animation if it doesn't already exist.
		/// </summary>
		/// <param name="pane" type="Object" mayBeNull="false">
		/// The pane is an object of the form {header, content, animation} corresponding to
		/// that pane's header section, content section, and the animation used to open and
		/// close its content section.  The content element is a new div that has been created
		/// to wrap the original div (so we can completely collapse it - even if it has
		/// padding, margins, etc.) which is pointed to by a dynamic _original property. The
		/// header element has a dynamic _index property indicating its position in the
		/// Accordion's pane collection (used primarily by the headers' shared click
		/// handler). Furthermore, the animation will either be an instance of
		/// LengthAnimation or ParallelAnimation (in the latter case, it will have two
		/// children which are a LengthAnimation and a FadeAnimation).  There will be two
		/// dynamic properties _length and _fade pointing to each of these children (to
		/// easily set the length and fadeEffect properties).  There is also a dynamic _ended
		/// property which is an event handler to be fired when the animation is complete,
		/// a dynamic _opening property to indicate whether the animation was opening or
		/// closing the pane, and a dynamic _pane property to provide a reference to the pane
		/// that was being animated.
		/// </param>
		/// <returns type="AjaxControlToolkit.Animation.Animation">
		/// Animation for the desired section
		/// </returns>

		var animation = pane.animation;
		if (!animation) {
			// Determine whether or not to just use the length animation or build a
			// composite effect with fading transitions (note that only the parent
			// animation has the duration/fps specfied)
			var length = null;
			var fade = null;
			if (!this._fadeTransitions) {
				animation = length = new AjaxControlToolkit.Animation.LengthAnimation(pane.content, this._duration, this._framesPerSecond, "style", "height", 0, 0, "px");
			} else {
				length = new AjaxControlToolkit.Animation.LengthAnimation(null, null, null, "style", "height", 0, 0, "px");
				fade = new AjaxControlToolkit.Animation.FadeAnimation(null, null, null, AjaxControlToolkit.Animation.FadeEffect.FadeOut, 0, 1, false);
				animation = new AjaxControlToolkit.Animation.ParallelAnimation(pane.content, this._duration, this._framesPerSecond, [fade, length]);
			}

			// Create references to the length and fade animations so we can easily
			// set the length and fadeEffect properties when animating without having
			// to reach into the composite animation.
			pane.animation = animation;
			animation._length = length;
			animation._fade = fade;
			animation._pane = pane;
			animation._opening = true;
			animation._behavior = this;
			animation._ended = Function.createDelegate(pane.animation, this._onAnimationFinished);
			animation.add_ended(pane.animation._ended);

			animation.initialize();
		}
		return animation;
	},

	_onAnimationFinished: function() {
		/// <summary>
		/// _onAnimationFinished is an event handler played after an animation (to open/
		/// close an accordion pane) has completed.  The delegate for this function should
		/// have associated it with an animation (so the this references below are expecting
		/// to reach expando fields declared on the animation).  It invokes _endPaneChange
		/// for the current pane.
		/// </summary>
		/// <returns />
		this._behavior._endPaneChange(this._pane, this._opening);
	},

	_initializeLayout: function() {
		/// <summary>
		/// Setup the layout of the accordion (either when the behavior is created or when the
		/// AutoSize mode is changed).
		/// </summary>
		/// <returns />

		// Stop any animations that are still playing (i.e. that haven't finished opening
		// or closing from changing previous panes) in case the user changed the AutoSize
		// mode while an animation was playing.
		for (var i = 0; i < this._panes.length; i++) {
			var animation = this._panes[i].animation;
			if (animation && animation.get_isPlaying()) {
				animation.stop();
			}
		}

		// Cache the initial size of the accordion
		var accordion = this.get_element();
		this._initialHeight = accordion.offsetHeight;
		var style = accordion.style;


		// Initialize the accordion itself
		if (this._autoSize === AjaxControlToolkit.AutoSize.None) {
			// Remove the window resizing handler
			this._disposeResizeHandler();

			// IE7 appears to have a rendering quirk where it will hide the
			// accordion if we change height or overflow from the empty string
			// to auto when it's contained in a fixed size div.  We'll just not
			// change the value if that's the case.
			var isIE7 = (Sys.Browser.agent == Sys.Browser.InternetExplorer && Sys.Browser.version >= 7)
			if (!isIE7 || (isIE7 && style.height && style.height.length > 0)) {
				style.height = 'auto';
			}
			if (!isIE7 || (isIE7 && style.overflow && style.overflow.length > 0)) {
				style.overflow = 'auto';
			}
		} else {
			// Add the window's resizing handler
			this._addResizeHandler();

			style.height = accordion.offsetHeight + 'px';
			style.overflow = 'hidden';
		}

		// Setup the layout attributes for the pane so that it will be in a proper opened
		// or closed state
		for (var i = 0; i < this._panes.length; i++) {
			this._initializePane(i);
		}

		// Resize the selected pane so (depending on the AutoSize mode) it will fill the
		// available remaining space after the headers have been laid out.
		this._resizeSelectedPane();
	},


	_initializePane: function(index) {
		/// <summary>
		/// Setup the layout attributes for the pane so that it will be in a proper opened or
		/// closed.  This will be called when adding a new pane for the first time or when
		/// changing the AutoSize mode.
		/// </summary>
		/// <param name="index" type="Number" integer="true">
		/// Index of the pane to initialize
		/// </param>
		/// <returns />

		var pane = this.get_Pane(index);
		if (!pane) {
			return;
		}
		var wrapper = pane.content;
		var original = wrapper._original;

		var opened = (index === this._selectedIndex);
		wrapper.style.height = (opened || (this._autoSize === AjaxControlToolkit.AutoSize.Fill)) ? 'auto' : '0px';
		wrapper.style.overflow = opened ? 'auto' : 'hidden';
		wrapper.style.display = opened ? 'block' : 'none';
		original.style.height = 'auto';
		original.style.maxHeight = '';
		original.style.overflow = opened ? 'auto' : 'hidden';

		var opacity = (opened || !this._fadeTransitions) ? 1 : 0;
		if (this._autoSize === AjaxControlToolkit.AutoSize.Fill) {
			if ($common.getElementOpacity(original) != opacity) {
				$common.setElementOpacity(original, opacity);
			}
			if ($common.getElementOpacity(wrapper) != 1) {
				$common.setElementOpacity(wrapper, 1);
			}
		} else {
			if ($common.getElementOpacity(wrapper) != opacity) {
				$common.setElementOpacity(wrapper, opacity);
			}
			if ($common.getElementOpacity(original) != 1) {
				$common.setElementOpacity(original, 1);
			}
		}
	},

	_addResizeHandler: function() {
		/// <summary>
		/// Attach the resize handler
		/// </summary>
		/// <returns />
		/// <remarks>
		/// This has been pulled out into its own method since we need to selectively wire
		/// up the resize handler depending on the AutoSize mode.
		/// </remarks>

		if (!this._resizeHandler) {
			this._resizeHandler = Function.createDelegate(this, this._resizeSelectedPane);
			$addHandler(window, "resize", this._resizeHandler);
		}
	},

	dispose: function() {
		/// <summary>
		/// Dispose of the AccordionBehavior
		/// </summary>
		/// <returns />

		// Remove the window resizing handler
		this._disposeResizeHandler();

		// Wipe all the animations
		this._disposeAnimations();

		// Wipe the _panes collection.  We're careful to wipe any expando properties
		// which could cause memory leaks in IE6.
		for (var i = this._panes.length - 1; i >= 0; i--) {
			var pane = this._panes[i];
			if (pane) {
				if (pane.header) {
					pane.header._index = null;
					$removeHandler(pane.header, "click", this._headerClickHandler);
					pane.header = null;
				}
				if (pane.content) {
					pane.content._original = null;
					pane.content = null;
				}
				this._panes[i] = null;
				delete this._panes[i];
			}
		}
		this._panes = null;
		this._headerClickHandler = null;

		AjaxControlToolkit.AccordionBehavior.callBaseMethod(this, 'dispose');
	},

	_disposeResizeHandler: function() {
		/// <summary>
		/// Remove the resize handler
		/// </summary>
		/// <returns />
		/// <remarks>
		/// This has been pulled out into its own method since we need to selectively wire
		/// up the resize handler depending on the AutoSize mode.
		/// </remarks>

		if (this._resizeHandler) {
			$removeHandler(window, "resize", this._resizeHandler);
			this._resizeHandler = null;
		}
	},

	_disposeAnimations: function() {
		/// <summary>
		/// Dispose all the animations.  This method was pulled out of dispose so we could
		/// allow the user to change the FadeTransitions property after the behavior was
		/// already initialized.  We can merge it back into dispose once we support generic
		/// animations on the Accordion.
		/// </summary>
		/// <returns />

		for (var i = 0; i < this._panes.length; i++) {
			var animation = this._panes[i].animation;
			if (animation) {
				// Stop the animation if it was still playing
				if (animation.get_isPlaying()) {
					animation.stop();
				}

				// Remove the event handler that runs after the animation completes
				if (animation._ended) {
					animation.remove_ended(animation._ended);
					animation._ended = null;
				}

				// Dispose the animation's resources (timer, etc.)
				animation.dispose();

				// Wipe expando properties to prevent leaks in IE6
				animation._length = null;
				animation._fade = null;
				animation._pane = null;
				animation._opening = null;
				animation._behavior = null;
				this._panes[i].animation = null;
			}
		}
	},

	_resizeSelectedPane: function() {
		/// <summary>
		/// Adjust the size of the currently selected pane (upon initialization,
		/// resizing the window, etc.)
		/// </summary>
		/// <returns />

		var pane = this.get_Pane();
		if (!pane) {
			return;
		}

		// Cache the header size so it only gets looked up when the window resizes
		this._headersSize = this._getHeadersSize().height;

		var original = pane.content._original;
		switch (this._autoSize) {
			case AjaxControlToolkit.AutoSize.None:
				original.style.height = 'auto';
				original.style.maxHeight = '';
				break;
			case AjaxControlToolkit.AutoSize.Limit:
				var remaining = this._getRemainingHeight(false);
				original.style.height = 'auto';
				original.style.maxHeight = remaining + 'px';
				break;
			case AjaxControlToolkit.AutoSize.Fill:
				var remaining = this._getRemainingHeight(true);
				original.style.height = remaining + 'px';
				original.style.maxHeight = '';
				break;
		}
	},

	_onHeaderClick: function(evt) {
		/// <summary>
		/// OnClick handler to open the desired pane
		/// </summary>
		/// <param name="evt" type="Sys.UI.DomEvent" mayBeNull="false">Event info</param>
		/// <returns />

		// Get the header that raised the event (by walking up the event target's
		// control hierarchy until we find an element just below the root of the accordion)
		var header = evt.target;
		var accordion = this.get_element();
		while (header && (header.parentNode !== accordion)) {
			header = header.parentNode;
		}

		// Stop the event from bubbling out of the header pane and prevent any
		// action from happening unless we clicked a control inside the header
		evt.stopPropagation();
		if (this._suppressHeaderPostbacks) {
			evt.preventDefault();
		}

		// Select this pane (which saves it to ClientState and initiates an animation
		// to show the content pane).  If the user clicked the currently open pane and
		// we're not required to keep one pane open, then we'll set the selected index
		// to -1 which will close it (which means all panes will be collapsed).
		var index = header._index;
		if ((index === this._selectedIndex) && !this._requireOpenedPane) {
			index = -1;
		}
		this._changeSelectedIndex(index, true);
	},

	_changeSelectedIndex: function(index, animate, force) {
		/// <summary>
		/// Change the accordion's selected pane to a new index (and optionally show the change).
		/// </summary>
		/// <param name="index" type="Number" integer="true" mayBeNull="false">
		/// Index of the new selected pane
		/// </param>
		/// <param name="animate" type="Boolean" mayBeNull="false">
		/// Whether or not to show the pane change (this is primarily intended to support
		/// restoring _selectedIndex in initialize before any panes have been added)
		/// </param>
		/// <param name="force" type="Boolean" mayBeNull="true" optional="true">
		/// We perform no action (i.e. raising events, animating, etc.) if the two indices represent
		/// the same pane (including the case when we have two different "no pane selected values"
		/// like -1 and -500).  The force flag is used during initialization to skip this check since
		/// we aren't able to determine invalid values yet.
		/// </param>
		/// <returns />

		// Don't bother doing anything if the index didn't change (we actually check the
		// panes so any invalid indices will match because get_Pane() will return null for
		// both of them)
		var lastIndex = this._selectedIndex;
		var currentPane = this.get_Pane(index);
		var lastPane = this.get_Pane(lastIndex);
		if (!force && (currentPane == lastPane)) {
			return;
		}

		// Raise the selectedIndexChanging event but don't change the selected index
		// if the handler set the cancel property to true
		var eventArgs = new AjaxControlToolkit.AccordionSelectedIndexChangeEventArgs(lastIndex, index);
		this.raiseSelectedIndexChanging(eventArgs);
		if (eventArgs.get_cancel()) {
			return;
		}

		//This sets the header CSS class to the non-selected case.
		if (lastPane) {
			lastPane.header.className = this._headerCssClass;
		}

		//This sets the selected header CSS class if available.
		if (currentPane) {
			currentPane.header.className = (this._headerSelectedCssClass == '') ?
                this._headerCssClass : this._headerSelectedCssClass;
		}

		this._selectedIndex = index;

		// Save the selected pane to preserve on postbacks
		this.set_ClientState(this._selectedIndex);

		// Animate the pane changes if required
		if (animate) {
			this._changePanes(lastIndex);
		}

		// Raise the selectedIndexChanged event and the propertyChanged event.  We include
		// both events because many users have expressed that the propertyChanged event is
		// not discoverable.
		this.raiseSelectedIndexChanged(new AjaxControlToolkit.AccordionSelectedIndexChangeEventArgs(lastIndex, index));
		this.raisePropertyChanged('SelectedIndex');
	},

	_changePanes: function(lastIndex) {
		/// <summary>
		/// The _changePanes function is used to animate the change between two panes when
		/// the selected index changes.  We will loop through each pane and get its
		/// animation (or demand create it if it doesn't have one yet), stop playing it if
		/// it's currently playing, change its parameters to either open or close, and then
		/// animate it.  Because we have an animation for each pane and we stop them if they
		/// were already playing, the Accordion has the ability to nicely change panes
		/// again before the animation is finished.
		/// </summary>
		/// <param name="lastIndex" type="Number" integer="true" mayBeNull="false">
		/// Index of the last selected Accordion pane
		/// </param>
		/// <returns />

		if (!this.get_isInitialized()) {
			return;
		}

		var open = null;
		var close = null;
		for (var i = 0; i < this._panes.length; i++) {
			// Get the animation for each pane (creating it on demand if it doesn't
			// already exist)
			var pane = this._panes[i];
			var animation = this._getAnimation(pane);

			// Stop any animations that are still playing (i.e. that haven't finished
			// opening or closing from changing previous panes)
			if (animation.get_isPlaying()) {
				animation.stop();
			}

			// If we're not opening or closing the current pane, then restart the loop.
			// We set the _opening flag so we don't have to keep checking if we're opening
			// or closing.  If opening, we also set the display style of the pane's content
			// so it will be visible for the animation
			if (i == this._selectedIndex) {
				animation._opening = true;
				open = animation;
			} else if (i == lastIndex) {
				animation._opening = false;
				close = animation;
			} else {
				continue;
			}

			// Get the pane ready to be animated by setting 
			this._startPaneChange(pane, animation._opening);


			// Setup the fade effect if we are using it
			if (this._fadeTransitions) {
				animation._fade.set_effect(animation._opening ? AjaxControlToolkit.Animation.FadeEffect.FadeIn : AjaxControlToolkit.Animation.FadeEffect.FadeOut);
			}

			// Set the length animation to either open or close depending on whether or
			// not this is the selected pane.  We also change the target to be the wrapper
			// or the original pane depending on whether the AutoSize mode is set to Fill
			// (because we need the background color, etc., to grow with the animation
			// which means changing the size of the original, not the wrapper, should grow).
			// We would prefer to animate the wrapper because it can collapse all the way to
			// 0px while the original can only collapse it's content size to 0px (leaving any
			// padding, margins, borders, etc.) which is why we need to factor in the size of
			// the original div's gutter pixels.  Animating the original content will also
			// cause the animation to jump slightly at the end when it collapses smoothly down
			// to the gutter pixels but then sets display: none and disappears.
			if (this._autoSize === AjaxControlToolkit.AutoSize.Fill) {
				animation.set_target(pane.content._original);
				animation._length.set_startValue($common.getContentSize(pane.content._original).height);
				animation._length.set_endValue(animation._opening ? this._getRemainingHeight(true) : 0);
			} else {
				animation.set_target(pane.content);
				animation._length.set_startValue(pane.content.offsetHeight);
				animation._length.set_endValue(animation._opening ? this._getRemainingHeight(false) : 0);
			}
		}

		// Play the animations to open the selected pane/close any other panes.  Note that we
		// pulled playing the animations out of the loop so that we could always play the
		// closing animation first.  If you play the opening animation first, in some cases the
		// accordion will always grow slightly larger (shifting the rest of the page down) and
		// then shrink again because the animations are running on different timers (and
		// unifying them into a single animation would be a lot messier than the current
		// implementation since they won't both play when we're closing the currently open pane).
		if (close) {
			close.play();
		}
		if (open) {
			open.play();
		}

		// TODO: Someday get both animations to run on the same timer because it makes an obvious
		// difference in IE6 and Safari
	},

	_startPaneChange: function(pane, opening) {
		/// <summary>
		/// Setup the pane before it is animated.
		/// </summary>
		/// <param name="pane" type="Object" mayBeNull="false">
		/// Pane that is being animated
		/// </param>
		/// <param name="opening" type="Boolean" mayBeNull="false">
		/// Whether or not the pane is being opened or closed
		/// </param>
		/// <returns />

		var wrapper = pane.content;
		var original = wrapper._original;

		if (opening) {
			// Make the hidden panes visible so we can see them animate
			wrapper.style.display = 'block';
		} else {
			// Hide any overflow because we'll be shrinking the wrapper div down to 0px and
			// we don't want content leaking out the bottom
			wrapper.style.overflow = 'hidden';

			// Turn off overflow on the original div because it's content doesn't grow during
			// the animation and leaving it on slows the animation down
			original.style.overflow = 'hidden';

			// Remove any explicit height off the original content section but manually set
			// the wrapper to the initial height (since it will be shrunk from this height
			// to zero)
			if (this._autoSize === AjaxControlToolkit.AutoSize.Limit) {
				wrapper.style.height = this._getTotalSize(original).height + 'px';
				original.style.maxHeight = '';
			}
		}
	},

	_endPaneChange: function(pane, opening) {
		/// <summary>
		/// Clean the pane up after it's been animated.
		/// </summary>
		/// <param name="pane" type="Object" mayBeNull="false">
		/// Pane that is being animated
		/// </param>
		/// <param name="opening" type="Boolean" mayBeNull="false">
		/// Whether or not the pane is being opened or closed
		/// </param>
		/// <returns />

		var wrapper = pane.content;
		var original = wrapper._original;

		if (opening) {
			// Depending on the mode, move the explicit height value from the original
			// content div to the wrapper div.  This is necessary because we moved the
			// explicit height value to the wrapper before the animation started since
			// it was the target.
			if (this._autoSize === AjaxControlToolkit.AutoSize.Limit) {
				var remaining = this._getRemainingHeight(true);
				original.style.maxHeight = remaining + 'px';
			}

			// Turn overflow back on so the original div's content can grow accordingly
			original.style.overflow = 'auto';

			// Remove an explicit height from the wrapper div so that it will
			// automatically grow and shrink with the original content div
			wrapper.style.height = 'auto';
			wrapper.style.overflow = 'auto';
		} else {
			// If we finished a close animation, completely hide the pane so that
			// it's content cannot be tabbed into
			wrapper.style.display = 'none';
		}
	},

	_getHeadersSize: function() {
		/// <summary>
		/// Compute the size of all the header sections
		/// </summary>
		/// <returns type="Object" mayBeNull="false">
		/// Size of all header sections (of the form {width, height}).
		/// </returns>

		// Compute the amount of space used by all the headers
		var total = { width: 0, height: 0 };
		for (var i = 0; i < this._panes.length; i++) {
			var size = this._getTotalSize(this._panes[i].header);
			total.width = Math.max(total.width, size.width);
			total.height += size.height;
		}
		return total;
	},

	_getRemainingHeight: function(includeGutter) {
		/// <summary>
		/// Determine how much remaining height we have to fill with the currently selected
		/// pane's content section after taking into account all the headers.  This is primarily
		/// used for the Limit and Fill AutoSize modes.
		/// </summary>
		/// <param name="includeGutter" type="Boolean" mayBeNull="false">
		/// Whether or not we should include the gutter (padding, borders, margins) of the
		/// selected pane's original content section.  This should be true whenever we're
		/// getting the remaining height for the original content section and false whenever
		/// we're getting the remaining height for its wrapper.
		/// </param>
		/// <returns type="Number" integer="true">
		/// Remaining height after all the headers have been accounted for
		/// </returns>    

		var height = 0;
		var pane = this.get_Pane();

		if (this._autoSize === AjaxControlToolkit.AutoSize.None) {
			// If the AutoSize mode is "None", then we use the size of the pane
			if (pane) {
				height = this._getTotalSize(pane.content._original).height;
			}
		} else {
			// Compute the amount of space used
			height = this._headersSize;
			if (includeGutter && pane) {
				height += this._getGutterSize(pane.content._original).height;
			}

			// Determine how much of the remaining space to use
			// (if AutoSize is "Fill", use the rest of the available space)
			var accordion = this.get_element();
			height = Math.max(accordion.offsetHeight - height, 0);

			// If AutoSize is "Limit", then the size of the pane should be either its
			// actual size, or the rest of the available space.
			if (pane && (this._autoSize === AjaxControlToolkit.AutoSize.Limit)) {
				var required = this._getTotalSize(pane.content._original).height;
				// Ensure we return a number greater than or equal to zero
				if (required > 0) {
					height = Math.min(height, required);
				}
			}
		}

		return height;
	},

	_getTotalSize: function(element) {
		/// <summary>
		/// Get the total size of an element, including its margins
		/// </summary>
		/// <param name="element" type="Sys.UI.DomElement" domElement="true">
		/// Element
		/// </param>
		/// <returns type="Object">
		/// Total size of the element (in the form {width, height})
		/// </returns>

		var size = $common.getSize(element);
		var box = $common.getMarginBox(element);
		size.width += box.horizontal;
		size.height += box.vertical;
		return size;
	},

	_getGutterSize: function(element) {
		/// <summary>
		/// Get the extra "gutter" size around an element made up of its padding,
		/// borders, and margins.
		/// </summary>
		/// <param name="element" type="Sys.UI.DomElement" domElement="true">
		/// Element
		/// </param>
		/// <returns type="Object">
		/// Size of the extra space (in the form of {height, width})
		/// </returns>

		var gutter = { width: 0, height: 0 };

		try {
			var box = $common.getPaddingBox(element);
			gutter.width += box.horizontal;
			gutter.height += box.vertical;
		} catch (ex) { }

		try {
			var box = $common.getBorderBox(element);
			gutter.width += box.horizontal;
			gutter.height += box.vertical;
		} catch (ex) { }

		var box = $common.getMarginBox(element);
		gutter.width += box.horizontal;
		gutter.height += box.vertical;

		return gutter;
	},

	add_selectedIndexChanging: function(handler) {
		/// <summary>
		/// Add an event handler for the selectedIndexChanging event
		/// </summary>
		/// <param name="handler" type="Function" mayBeNull="false">
		/// Event handler
		/// </param>
		/// <returns />
		this.get_events().addHandler('selectedIndexChanging', handler);
	},
	remove_selectedIndexChanging: function(handler) {
		/// <summary>
		/// Add an event handler for the selectedIndexChanging event
		/// </summary>
		/// <param name="handler" type="Function" mayBeNull="false">
		/// Event handler
		/// </param>
		/// <returns />
		this.get_events().removeHandler('selectedIndexChanging', handler);
	},
	raiseSelectedIndexChanging: function(eventArgs) {
		/// <summary>
		/// Raise the selectedIndexChanging event
		/// </summary>
		/// <param name="eventArgs" type="AjaxControlToolkit.AccordionSelectedIndexChangeEventArgs" mayBeNull="false">
		/// Event arguments for the selectedIndexChanging event
		/// </param>
		/// <returns />

		var handler = this.get_events().getHandler('selectedIndexChanging');
		if (handler) {
			handler(this, eventArgs);
		}
	},

	add_selectedIndexChanged: function(handler) {
		/// <summary>
		/// Add an event handler for the selectedIndexChanged event
		/// </summary>
		/// <param name="handler" type="Function" mayBeNull="false">
		/// Event handler
		/// </param>
		/// <returns />
		this.get_events().addHandler('selectedIndexChanged', handler);
	},
	remove_selectedIndexChanged: function(handler) {
		/// <summary>
		/// Add an event handler for the selectedIndexChanged event
		/// </summary>
		/// <param name="handler" type="Function" mayBeNull="false">
		/// Event handler
		/// </param>
		/// <returns />
		this.get_events().removeHandler('selectedIndexChanged', handler);
	},
	raiseSelectedIndexChanged: function(eventArgs) {
		/// <summary>
		/// Raise the selectedIndexChanged event
		/// </summary>
		/// <param name="eventArgs" type="AjaxControlToolkit.AccordionSelectedIndexChangeEventArgs" mayBeNull="false">
		/// Event arguments for the selectedIndexChanged event
		/// </param>
		/// <returns />

		var handler = this.get_events().getHandler('selectedIndexChanged');
		if (handler) {
			handler(this, eventArgs);
		}
	},

	get_Pane: function(index) {
		/// <summary>
		/// Get a specific Accordion pane given its index.  If no index is provided, get
		/// the currently selected pane.
		/// </summary>
		/// <param name="index" type="Number" integer="true" mayBeNull="true">
		/// Index of the desired Accordion pane.  If the index is not provided, we use
		/// the currently selected index.  In the event the provided index (or the currently
		/// selected index) is outside the bounds of the panes collection, we return null.
		/// </param>
		/// <returns type="Object" mayBeNull="true">
		/// The desired pane object, or null if outside the the bounds of the _panes array.
		/// The pane is an object of the form {header, content, animation} corresponding to
		/// that pane's header section, content section, and the animation used to open and
		/// close its content section.  The content element is a new div that has been
		/// created to wrap the original div (so we can completely collapse it - even if it
		/// has padding, margins, etc.) which is pointed to by a dynamic _original property.
		/// The header element has a dynamic _index property indicating its position in the
		/// Accordion's pane collection (used primarily by the headers' shared click handler).
		/// Furthermore, the animation will either be an instance of LengthAnimation or
		/// ParallelAnimation (in the latter case, it will have two children which are a
		/// LengthAnimation and a FadeAnimation).  There will be two dynamic properties
		/// _length and _fade pointing to each of these children (to easily set the length
		/// and fadeEffect properties).  There is also a dynamic _ended property which is an
		/// event handler to be fired when the animation is complete, a dynamic _opening
		/// property to indicate whether the animation was opening or closing the pane, and
		/// a dynamic _pane property to provide a reference to the pane that was being
		/// animated.
		/// </returns>

		if (index === undefined || index === null) {
			index = this._selectedIndex;
		}
		return (this._panes && index >= 0 && index < this._panes.length) ? this._panes[index] : null;
	},

	get_Count: function() {
		/// <value type="Number" integer="true" mayBeNull="false">
		/// Number of Accordion panes
		/// </summary>
		return this._panes ? this._panes.length : 0;
	},

	get_TransitionDuration: function() {
		/// <value type="Number">
		/// Length of time to transition between Accordion sections in
		/// milleseconds.  The default value is 250ms.
		/// </value>
		return this._duration * 1000;
	},
	set_TransitionDuration: function(value) {
		if (this._duration != (value / 1000)) {
			this._duration = value / 1000;
			for (var i = 0; i < this._panes.length; i++) {
				var animation = this._panes[i].animation;
				if (animation) {
					animation.set_duration(this._duration);
				}
			}
			this.raisePropertyChanged('TransitionDuration');
		}
	},

	get_FramesPerSecond: function() {
		/// <value type="Number" integer="true">
		/// Number of steps per second in the transition animations.
		/// The default value is 30 frames per second.
		/// </value>
		return this._framesPerSecond;
	},
	set_FramesPerSecond: function(value) {
		if (this._framesPerSecond != value) {
			this._framesPerSecond = value;
			for (var i = 0; i < this._panes.length; i++) {
				var animation = this._panes[i].animation;
				if (animation) {
					animation.set_fps(this._framesPerSecond);
				}
			}
			this.raisePropertyChanged('FramesPerSecond');
		}
	},

	get_FadeTransitions: function() {
		/// <value type="Boolean">
		/// Whether or not to fade the accordion panes when transitioning
		/// </value>
		return this._fadeTransitions;
	},
	set_FadeTransitions: function(value) {
		if (this._fadeTransitions != value) {
			this._fadeTransitions = value;

			// Whenever we toggle FadeTransitions we dispose the entire list of
			// animations because it's not easy to selectively play just one of a
			// ParallelAnimation's children...  This is of course just a temporary
			// workaround until we enable generic animations and remove the
			// FadeTransitions property.
			this._disposeAnimations();

			// Set all the content sections to 100% opacity in case they had been
			// faded out when a pane was changed but not faded back in yet
			if (!this._fadeTransitions) {
				for (var i = 0; i < this._panes.length; i++) {
					if ($common.getElementOpacity(this._panes[i].content) != 1) {
						$common.setElementOpacity(this._panes[i].content, 1);
					}
					if ($common.getElementOpacity(this._panes[i].content._original) != 1) {
						$common.setElementOpacity(this._panes[i].content._original, 1);
					}
				}
			}
			this.raisePropertyChanged('FadeTransitions');
		}
	},

	get_HeaderCssClass: function() {
		/// <summary>
		/// This is the CSS class applied to each header.
		/// </summary>
		return this._headerCssClass;
	},

	set_HeaderCssClass: function(value) {
		this._headerCssClass = value;
		this.raisePropertyChanged('HeaderCssClass');
	},

	get_HeaderSelectedCssClass: function() {
		/// <summary>
		/// This is the CSS class applied to the selected header.
		/// </summary>
		return this._headerSelectedCssClass;
	},

	set_HeaderSelectedCssClass: function(value) {
		this._headerSelectedCssClass = value;
		this.raisePropertyChanged('HeaderSelectedCssClass');
	},

	get_ContentCssClass: function() {
		/// <summary>
		/// This is the CSS class applied to each content section.
		/// </summary>
		return this._contentCssClass;
	},

	set_ContentCssClass: function(value) {
		this._contentCssClass = value;
		this.raisePropertyChanged('ContentCssClass');
	},

	get_AutoSize: function() {
		/// <value type="AjaxControlToolkit.AutoSize">
		/// AutoSize mode
		/// </value>
		return this._autoSize;
	},
	set_AutoSize: function(value) {
		// In IE, treat AutoSize.Limit exactly the same as AutoSize.Fill.  Since IE does not
		// support the max-height CSS property, we can't achieve the Limit auto-size behavior
		// without fixing the exact size of the pane (which means it can't grow/shrink in response
		// to dynamic content changes).  Since Fill has most of the same UI characteristics, it's
		// a safer and cleaner workaround for this one browser.
		if (Sys.Browser.agent === Sys.Browser.InternetExplorer && value === AjaxControlToolkit.AutoSize.Limit) {
			value = AjaxControlToolkit.AutoSize.Fill;
		}

		if (this._autoSize != value) {
			this._autoSize = value;
			this._initializeLayout();
			this.raisePropertyChanged('AutoSize');
		}
	},

	get_SelectedIndex: function() {
		/// <value type="Number" integer="true">
		/// Index of the currently selected Accordion section
		/// </value>
		return this._selectedIndex;
	},
	set_SelectedIndex: function(value) {
		this._changeSelectedIndex(value, true);
	},

	get_requireOpenedPane: function() {
		/// <value type="Boolean">
		/// Whether or not clicking the header will close the currently opened pane
		/// (which leaves all the Accordion's panes closed)
		/// </value>
		return this._requireOpenedPane;
	},
	set_requireOpenedPane: function(value) {
		if (this._requireOpenedPane != value) {
			this._requireOpenedPane = value;
			this.raisePropertyChanged('requireOpenedPane');
		}
	},

	get_suppressHeaderPostbacks: function() {
		/// <value type="Boolean">
		/// Whether or not we suppress the client-side click handlers of any elements
		/// (including server controls like Button or HTML elements like anchor) in the
		/// header sections of the Accordion.
		/// </value>
		return this._suppressHeaderPostbacks;
	},
	set_suppressHeaderPostbacks: function(value) {
		if (this._suppressHeaderPostbacks != value) {
			this._suppressHeaderPostbacks = value;
			this.raisePropertyChanged('suppressHeaderPostbacks');
		}
	}
}
AjaxControlToolkit.AccordionBehavior.registerClass('AjaxControlToolkit.AccordionBehavior', AjaxControlToolkit.BehaviorBase);
